#ifndef __QUANTITATE_H__
#define __QUANTITATE_H__

#include <iostream>
#include <math.h>
#include <set>
#include "DisplayFormat.h"

using namespace std;


const double FLT_EPSILON = exp(-10);


template<typename Unit>
class Quantitate
{
public:
 
    Quantitate<Unit>(float amount, const Unit& unit):amount_m(amount), unit_m(unit){};
    Quantitate<Unit>(const Quantitate<Unit>& quantitate):amount_m(quantitate.amount_m), unit_m(quantitate.unit_m), displayFormat_m (quantitate.displayFormat_m){};
    Quantitate<Unit>(float amount, const Unit& unit, DisplayFormat* dispFormat):amount_m(amount), unit_m(unit), displayFormat_m(dispFormat){};
 
    Quantitate<Unit>& operator=(const Quantitate<Unit>& quantitateA);

    friend bool operator== (const Quantitate<Unit>& quantitateA, const Quantitate<Unit>& quantitateB)
    {
         return fabs(quantitateA.convertToBenchmark() - quantitateB.convertToBenchmark()) < FLT_EPSILON;
    }

    friend Quantitate<Unit> operator+ (const Quantitate<Unit>& quantitateA, const Quantitate<Unit>& quantitateB)
    {
     float Denominator = (quantitateA.unit_m.convertToBenchmark() < quantitateB.unit_m.convertToBenchmark())?   \
       (quantitateA.unit_m.convertToBenchmark()) : (quantitateB.unit_m.convertToBenchmark());
     const Unit& unitTmp =  (quantitateA.unit_m.convertToBenchmark() < quantitateB.unit_m.convertToBenchmark())?   \
       (quantitateA.unit_m):(quantitateB.unit_m);

     return Quantitate<Unit>((quantitateA.convertToBenchmark() + quantitateB.convertToBenchmark())/Denominator, unitTmp);
    }

    friend Quantitate<Unit> operator- (const Quantitate<Unit>& quantitateA, const Quantitate<Unit>& quantitateB)
    {
     float Denominator = (quantitateA.unit_m.convertToBenchmark() < quantitateB.unit_m.convertToBenchmark())?   \
       (quantitateA.unit_m.convertToBenchmark()) : (quantitateB.unit_m.convertToBenchmark());
     const Unit& unitTmp =  (quantitateA.unit_m.convertToBenchmark() < quantitateB.unit_m.convertToBenchmark())?   \
       (quantitateA.unit_m):(quantitateB.unit_m);

     return Quantitate<Unit>((quantitateA.convertToBenchmark() - quantitateB.convertToBenchmark())/Denominator, unitTmp);
    }

    friend bool operator> (const Quantitate<Unit>& quantitateA, const Quantitate<Unit>& quantitateB) 
    { 
        return quantitateA.convertToBenchmark() > quantitateB.convertToBenchmark();
    }

 
    friend bool operator< (const Quantitate<Unit>& quantitateA, const Quantitate<Unit>& quantitateB) 
    {
        return quantitateA.convertToBenchmark() < quantitateB.convertToBenchmark();
    }

    friend bool operator!= (const Quantitate<Unit>& quantitateA, const Quantitate<Unit>& quantitateB)
    {
        return fabs(quantitateA.convertToBenchmark() - quantitateB.convertToBenchmark()) >= FLT_EPSILON;
     }

    friend ostream& operator<<(ostream& os, const Quantitate<Unit>& quantitate)
    {
     if (quantitate.displayFormat_m)
         (quantitate.displayFormat_m)->toStringFormat(os, quantitate.convertToBenchmark());
     else 
         os << "please choose display format then print this quantitate system" << endl;
     return os;
    }
    
     
private:

    Quantitate<Unit> operator/ (const Quantitate<Unit>& quantitateA);

    Quantitate<Unit> operator% (const Quantitate<Unit>& quantitateA);

    bool isUnitConsistent(const Quantitate<Unit>& quantitateB) const;
    float convertToBenchmark() const;

    float amount_m;
    const Unit& unit_m;

    DisplayFormat*  displayFormat_m;

  
};

template<typename Unit> float 
Quantitate<Unit>::convertToBenchmark() const
{
  return (amount_m * unit_m.convertToBenchmark());
}

template<typename Unit> bool 
Quantitate<Unit>::isUnitConsistent(const Quantitate<Unit>& quantitateB) const
{
  return unit_m.isSameUnitType(quantitateB.unit_m);
}


template<typename Unit> Quantitate<Unit>& 
Quantitate<Unit>::operator=(const Quantitate<Unit>& quantitateA)
{
    amount_m = quantitateA.amount_m;
    (const_cast<Unit&>(unit_m)) = (quantitateA.unit_m);
    return (*this);
}

template<typename Unit> Quantitate<Unit> 
Quantitate<Unit>::operator/ (const Quantitate<Unit>& quantitateA)
{
    return Quantitate<Unit>((int)(convertToBenchmark()/quantitateA.convertToBenchmark()), quantitateA.unit_m);
}

template<typename Unit> Quantitate<Unit> 
Quantitate<Unit>::operator% (const Quantitate<Unit>& quantitateA)
{
    const Unit& unitTmp = (convertToBenchmark() < quantitateA.convertToBenchmark())? (unit_m):(quantitateA.unit_m);

    if (convertToBenchmark() >= quantitateA.convertToBenchmark())
        const Unit& unitTmp = (unit_m.convertToBenchmark() < quantitateA.unit_m.convertToBenchmark())? (unit_m):(quantitateA.unit_m);
 
    return Quantitate<Unit>(fmod(convertToBenchmark(), quantitateA.convertToBenchmark())/(unitTmp.convertToBenchmark()), unitTmp);
}


#endif
